home *** CD-ROM | disk | FTP | other *** search
/ Libris Britannia 4 / science library(b).zip / science library(b) / TECHNICA / COMPUTER / H254.ZIP / IRITSM3S.ZIP / POLY3D-R / EVALCOLR.C < prev    next >
C/C++ Source or Header  |  1992-01-02  |  20KB  |  531 lines

  1. /*****************************************************************************
  2. *   Routines to    evaluate vertices colors for all polygons in data. It is     *
  3. * assumed the parser has updated plane equations for all polygons at this    *
  4. * stage (if from reading it in, or by regenerating it), and they were mapped *
  5. * correctly using the global transformation applied to them.             *
  6. *   The vertices are updated for both Flat or Gouraud shading, and color is  *
  7. * evaluated for them, as a color index into the color table. Because of the  *
  8. * way the table is constructed and because two vertices in same polygon can  *
  9. * have two levels of same color, it is o.k. to interplated the color         *
  10. * indices (We assume no specular affects)!                     *
  11. *   As side affect, we use this pass to hash the polygon into global hash    *
  12. * table PolyHasTable, sorted by their Ymin values.                 *
  13. *                                         *
  14. * Written by:  Gershon Elber                Ver 2.0, Mar. 1990   *
  15. *****************************************************************************/
  16.  
  17. #ifdef __MSDOS__
  18. #include <stdlib.h>
  19. #endif /* __MSDOS__ */
  20.  
  21. #include <math.h>
  22. #include <stdio.h>
  23. #include <string.h>
  24. #include <time.h>
  25. #include "program.h"
  26. #include "iritprsr.h"
  27.  
  28. #define    MIN_VERTEX_HASH_SIZE    1000  /* Minimum number of entries in table. */
  29.  
  30. typedef struct VertexHashStruct {     /* Used to hash an object vertices. */
  31.     int Entry;
  32.     RealType Normal[3];
  33.     IPVertexStruct *PVertex;
  34. } VertexHashStruct;
  35.  
  36. static RealType MinNormalDotProd; /* Min. normal dot prod to accept as same. */
  37. static int VertexHashSize = 0;    /* Size of vertex hash table size allocated. */
  38. static int PolyCount = 0;    /* Number of polygons processed in this module: */
  39.  
  40. static void PrepareAllObjects(IPObjectStruct *PObject);
  41. static void PrepareOneObject(IPObjectStruct *PObject);
  42. static void AverageVrtxNormals(IPObjectStruct *PObject);
  43. static void InsertVerticesToHashTable(VertexHashStruct *VerticesHashTable,
  44.                               IPObjectStruct *PObject);
  45. static void UpdateVerticesFromHashTable(VertexHashStruct *VerticesHashTable,
  46.                               IPObjectStruct *PObject);
  47. static int SameVertexAndNormals(IPVertexStruct *PVertex1, RealType *Normal1,
  48.                           VertexHashStruct *PHVertex2);
  49. static RealType EvalNormalsAngle(IPVertexStruct *PVertex1, RealType *Normal1,
  50.                           VertexHashStruct *PHVertex2);
  51. static int EvalVertexKey(IPVertexStruct *PVertex);
  52. static void EvalOnePolygon(IPPolygonStruct *PPolygon, int ColorIndex);
  53. static int CosineShading(RealType *Normal, int ColorIndex);
  54.  
  55. /*****************************************************************************
  56. * Routine to evaluate the vertices colors for both Flat/Gouraud shading, as  *
  57. * indices for the color table.                             *
  58. *****************************************************************************/
  59. void EvalVrtxColors(IPObjectStruct *PObjects)
  60. {
  61.     int    i;
  62.     long
  63.     SaveTime = time(NULL);
  64.  
  65.     PolyHashTable = (IPPolygonStruct **)
  66.     MyMalloc(sizeof(IPPolygonStruct *) * GlblShadeInfo.ScrnYSize *
  67.                         GlblShadeInfo.SubSamplePixel);
  68.  
  69.     for (i = 0;
  70.      i < GlblShadeInfo.ScrnYSize * GlblShadeInfo.SubSamplePixel;
  71.      i++)
  72.     PolyHashTable[i] = NULL;
  73.  
  74.     fprintf(stderr, "\nPass 3, Polys [%4d] =      ", GlblNumOfPolys);
  75.  
  76.     PrepareAllObjects(PObjects);
  77.  
  78.     fprintf(stderr, ",  %ld seconds.", time(NULL) - SaveTime);
  79. }
  80.  
  81. /*****************************************************************************
  82. * Scan all objects.                                 *
  83. *****************************************************************************/
  84. static void PrepareAllObjects(IPObjectStruct *PObjects)
  85. {
  86.     while (PObjects) {
  87.     PrepareOneObject(PObjects);
  88.     PObjects = PObjects -> Pnext;
  89.     }
  90. }
  91.  
  92. /*****************************************************************************
  93. * Routine to prepare one object Object.                         *
  94. *****************************************************************************/
  95. static void PrepareOneObject(IPObjectStruct *PObject)
  96. {
  97.     static int
  98.     WasPolyline = FALSE;
  99.     int Level,
  100.     ColorIndex = PObject -> Color;
  101.     IPPolygonStruct *Ptemp,
  102.     *PList = PObject -> U.PPolygon;
  103.  
  104.     if (GlblShadeInfo.Gouraud) {
  105.     /* Need to average all vertices normals from adjacent polygons,      */
  106.     /* so we can interpolate them, and select color for them directly.   */
  107.     AverageVrtxNormals(PObject);
  108.     }
  109.  
  110.     while (PList) {
  111.     Ptemp = PList -> Pnext;
  112.  
  113.     EvalOnePolygon(PList, ColorIndex);
  114.  
  115.     /* And add polygon into polygon hash table sorted by Ymin: */
  116.     switch (PList -> Type) {
  117.         case IP_POLYGON:
  118.         /* If BackFacing is TRUE, and this polygon has plane         */
  119.         /* suggesting it is back facing - dont insert to hash table. */
  120.         if (!(GlblShadeInfo.BackFacing && PList -> Plane[2] > 0.0)) {
  121.             Level = PList -> Ymin;
  122.             Level = BOUND(Level, 0, GlblShadeInfo.ScrnYSize *
  123.                  GlblShadeInfo.SubSamplePixel); /* Be 100% safe. */
  124.             if (Level < GlblShadeInfo.ScrnYSize *
  125.                         GlblShadeInfo.SubSamplePixel) {
  126.             /* Concat to hash table at level Level: */
  127.             PList -> Pnext = PolyHashTable[Level];
  128.             PolyHashTable[Level] = PList;
  129.             }
  130.         }
  131.         else {
  132.             if (!GlblShadeInfo.Gouraud) PolyCount--;/* One less poly.*/
  133.         }
  134.         break;
  135.         case IP_POLYLINE:
  136.         if (!WasPolyline) {
  137.             WasPolyline = TRUE;
  138.             if (GlblMore)
  139.             fprintf(stderr,
  140.                 "\nPolylines are not supported, ignored.\n");
  141.         }
  142.         break;
  143.     }
  144.  
  145.     PList =    Ptemp;
  146.     }
  147. }
  148.  
  149. /*****************************************************************************
  150. * Routine to update average normals of adjacent polygons at common vertices  *
  151. * so gouraud shading can be applied. Does 2 passes on all object vertices:   *
  152. * 1. Combine (and average) common vertices - vertices with normal no differ  *
  153. *    more than GlblShadeInfo.NrmlAvgDegree degrees.                 *
  154. * 2. For each vertex find its closest averaged normal as evaluated at 1, and *
  155. *    calculate color for it.                             *
  156. *****************************************************************************/
  157. static void AverageVrtxNormals(IPObjectStruct *PObject)
  158. {
  159.     int i;
  160.     VertexHashStruct *VerticesHashTable;
  161.  
  162.     /* Prepare maximum degree allowed to merge two normals in cosine form:   */
  163.     MinNormalDotProd = cos(GlblShadeInfo.NrmlAvgDegree * M_PI / 180.0);
  164.  
  165.     /* Allocate hash table twice as big as number of possible entries to     */
  166.     /* reduce the average hit ratio, with minimum of MIN_VERTEX_HASH_SIZE.   */
  167.     VertexHashSize = MAX(GlblNumOfVerts * 2, MIN_VERTEX_HASH_SIZE);
  168.     VerticesHashTable = (VertexHashStruct *)
  169.     MyMalloc(VertexHashSize * sizeof(VertexHashStruct));
  170.     for (i = 0; i < VertexHashSize; i++) VerticesHashTable[i].Entry = 0;
  171.  
  172.     InsertVerticesToHashTable(VerticesHashTable, PObject);
  173.  
  174.     UpdateVerticesFromHashTable(VerticesHashTable, PObject);
  175.  
  176.     free((char *) VerticesHashTable);
  177. }
  178.  
  179. /*****************************************************************************
  180. * Routine to insert all vertices in object into the hash table. Each vertex  *
  181. * is entered at place (key) as select via EvalVertexKey routine. If no place *
  182. * the next ones are scaned until free (NULL) is found (no double key fansy   *
  183. * hashing techniques...). Note however that while scanning the non NULL      *
  184. * entries the vertex position is compared for equality, and its normal for   *
  185. * equality upto AvgNormalDegree, and if both hold, the two are merged into   *
  186. * one vertex in that position but with averaged normal.                 *
  187. *****************************************************************************/
  188. static void InsertVerticesToHashTable(VertexHashStruct *VerticesHashTable,
  189.                               IPObjectStruct *PObject)
  190. {
  191.     int i, Key;
  192.     RealType EntryRatio, *Normal, *OldNormal;
  193.     IPPolygonStruct *PPolygon;
  194.     IPVertexStruct *PVertex;
  195.  
  196.     for (PPolygon = PObject -> U.PPolygon;
  197.      PPolygon != NULL;
  198.      PPolygon = PPolygon -> Pnext) {
  199.     Normal = PPolygon -> Plane;
  200.  
  201.     /* If BackFacing is TRUE, and this polygon has plane        */
  202.     /* suggesting it is back facing - dont count it.        */
  203.     if (!(GlblShadeInfo.BackFacing && PPolygon -> Plane[2] < 0.0))
  204.         PolyCount++;
  205.     fprintf(stderr, "\b\b\b\b\b%5d", PolyCount);
  206.  
  207.  
  208.     for (PVertex = PPolygon -> PVertex;
  209.          PVertex != NULL;
  210.          PVertex = PVertex -> Pnext) {
  211.         Key = EvalVertexKey(PVertex);
  212.         while (VerticesHashTable[Key].Entry != 0) {
  213.         /* Test if should be combined with old vertex: */
  214.         if (SameVertexAndNormals(PVertex, Normal,
  215.                             &VerticesHashTable[Key])) {
  216.             /* Megre the normals: */
  217.             EntryRatio = 1.0 / ++VerticesHashTable[Key].Entry;
  218.             OldNormal = VerticesHashTable[Key].Normal;
  219.             for (i = 0; i < 3; i++) OldNormal[i] =
  220.             OldNormal[i] * (1.0 - EntryRatio) +
  221.             Normal[i] * EntryRatio;
  222.             break;
  223.         }
  224.         Key = ++Key % VertexHashSize;
  225.         }
  226.         if (VerticesHashTable[Key].Entry == 0) {
  227.         /* Could not merge the vertex with old one - do it now: */
  228.         VerticesHashTable[Key].PVertex = PVertex;
  229.         GEN_COPY(VerticesHashTable[Key].Normal, Normal,
  230.                               3 * sizeof(RealType));
  231.         ++VerticesHashTable[Key].Entry;
  232.         }
  233.     }
  234.     }
  235. }
  236.  
  237. /*****************************************************************************
  238. * Routine to scan all vertices in object and update their normals to the     *
  239. * close one at that point as found in the hash table.                 *
  240. *****************************************************************************/
  241. static void UpdateVerticesFromHashTable(VertexHashStruct *VerticesHashTable,
  242.                               IPObjectStruct *PObject)
  243. {
  244.     static int
  245.     Flip = FALSE;
  246.     int Key;
  247.     RealType *Normal, MaxCosAngle, CosAngle;
  248.     IPPolygonStruct *PPolygon;
  249.     IPVertexStruct *PVertex;
  250.     VertexHashStruct *PHVertex;
  251.  
  252.     for (PPolygon = PObject -> U.PPolygon;
  253.      PPolygon != NULL;
  254.      PPolygon = PPolygon -> Pnext) {
  255.     Normal = PPolygon -> Plane;
  256.  
  257.     if (Flip) {
  258.         Flip = FALSE;
  259.         fprintf(stderr, "a\b");
  260.     }
  261.     else {
  262.         Flip = TRUE;
  263.         fprintf(stderr, "A\b");
  264.     }
  265.  
  266.     PVertex = PPolygon -> PVertex;
  267.     for (PVertex = PPolygon -> PVertex;
  268.          PVertex != NULL;
  269.          PVertex = PVertex -> Pnext) {
  270.         PHVertex = NULL;
  271.         MaxCosAngle = -INFINITY;
  272.         Key = EvalVertexKey(PVertex);
  273.         while (VerticesHashTable[Key].Entry != 0) {
  274.         /* Search for the closest Normal at this vertex point: */
  275.         if ((CosAngle = (EvalNormalsAngle(PVertex, Normal,
  276.                     &VerticesHashTable[Key]))) > MaxCosAngle) {
  277.             PHVertex = &VerticesHashTable[Key];
  278.             MaxCosAngle = CosAngle;
  279.         }
  280.         Key = ++Key % VertexHashSize;
  281.         }
  282.         if (IP_HAS_VRTX_NORMAL(PVertex)) {
  283.         /* Use defined normal for this vertex, if has one. */
  284.         PVertex -> Color = CosineShading(PVertex -> Normal,
  285.                             PObject -> Color);
  286.         }
  287.         else if (PHVertex) {
  288.         /* Should always be non NULL but who knows... */
  289.         PVertex -> Color = CosineShading(PHVertex -> Normal,
  290.                             PObject -> Color);
  291.         }
  292.         else {
  293.         PVertex -> Color = CosineShading(PPolygon -> Plane,
  294.                             PObject -> Color);
  295.         }
  296.     }
  297.     }
  298. }
  299.  
  300. /*****************************************************************************
  301. * Routine to compare two vertices to be exactly equal in their position and  *
  302. * to have same normal up to GlblShadeInfo.NrmlAvgDegree.             *
  303. *****************************************************************************/
  304. static int SameVertexAndNormals(IPVertexStruct *PVertex1, RealType *Normal1,
  305.                         VertexHashStruct *PHVertex2)
  306. {
  307.     RealType *Normal2,
  308.     *Coord1 = PVertex1 -> Coord,
  309.     *Coord2 = PHVertex2 -> PVertex -> Coord;
  310.  
  311.     if (!APX_EQ(Coord1[0], Coord2[0]) ||
  312.     !APX_EQ(Coord1[1], Coord2[1]) ||
  313.     !APX_EQ(Coord1[2], Coord2[2])) return FALSE;
  314.  
  315.     Normal2 = PHVertex2 -> Normal;
  316.  
  317.     return (Normal1[0] * Normal2[0] +
  318.         Normal1[1] * Normal2[1] +
  319.         Normal1[2] * Normal2[2] > MinNormalDotProd);
  320. }
  321.  
  322. /*****************************************************************************
  323. * Routine to evaluate the angle between the given two vertices if they are   *
  324. * the same, and return the angle cosine value. (-INFINITY is returned if     *
  325. * not same point...).                                 *
  326. *****************************************************************************/
  327. static RealType EvalNormalsAngle(IPVertexStruct *PVertex1, RealType *Normal1,
  328.                         VertexHashStruct *PHVertex2)
  329. {
  330.     RealType *Normal2,
  331.     *Coord1 = PVertex1 -> Coord,
  332.     *Coord2 = PHVertex2 -> PVertex -> Coord;
  333.  
  334.     if (!APX_EQ(Coord1[0], Coord2[0]) ||
  335.     !APX_EQ(Coord1[1], Coord2[1]) ||
  336.     !APX_EQ(Coord1[2], Coord2[2])) return -INFINITY;
  337.  
  338.     Normal2 = PHVertex2 -> Normal;
  339.  
  340.     return Normal1[0] * Normal2[0] +
  341.        Normal1[1] * Normal2[1] +
  342.        Normal1[2] * Normal2[2];
  343. }
  344.  
  345. /*****************************************************************************
  346. * Routine to evaluate integer key in the range 0 .. VertexHashSize - 1         *
  347. * for the given vertex PVertex.                             *
  348. *****************************************************************************/
  349. static int EvalVertexKey(IPVertexStruct *PVertex)
  350. {
  351.     int Key;
  352.     RealType
  353.     *Coord = PVertex -> Coord;
  354.  
  355.     Key = ((int) (((long) (Coord[0] * 239 + Coord[1] * 677 + Coord[2] * 109)) %
  356.                             VertexHashSize));
  357.  
  358.     return Key;
  359. }
  360.  
  361. /*****************************************************************************
  362. * Routine to update the vertices colors, which are indices tp the ColorMap   *
  363. * as save in global GlblShadeInfo structure. Given Object color (as index    *
  364. * into table), the exact level is evaluationed using each polygon normal.    *
  365. *****************************************************************************/
  366. static void EvalOnePolygon(IPPolygonStruct *PPolygon, int ColorIndex)
  367. {
  368.     int Color;
  369.     IPVertexStruct
  370.     *V = PPolygon -> PVertex;
  371.  
  372.     if (GlblShadeInfo.Gouraud) {
  373.     /* Vertices were already updated by the Object averaging of normals  */
  374.     /* routine (see AverageVrtxNormals routine).                 */
  375.     }
  376.     else {
  377.     fprintf(stderr, "\b\b\b\b\b%5d", ++PolyCount);
  378.  
  379.     /* This is much easier - set all vertices color to same Color.       */
  380.     Color = CosineShading(PPolygon -> Plane, ColorIndex);
  381.     for (; V != NULL; V = V -> Pnext) V -> Color = Color;
  382.     }
  383. }
  384.  
  385. /*****************************************************************************
  386. * Routine to evaluate cosine shading given a normal vector. Uses the global  *
  387. * shading structure GlblShadeInfo to evaluate it. Returns a color from the   *
  388. * color map defined for the current scene.                     *
  389. * It is assumed the given normal is normalized to size 1.0, and that the     *
  390. * intensity levels of the Object Color are saved in indices PObject -> Color *
  391. * to PObject -> Color + GlblShadeInfo.LevelsPerColor - 1. These colors are   *
  392. * interpolated to begin from the ambient level, so we dont have to consider  *
  393. * ambient light here - just the cosine factor.                     *
  394. *****************************************************************************/
  395. static int CosineShading(RealType *Normal, int ColorIndex)
  396. {
  397.     int NewColorIndex;
  398.     RealType Intensity;
  399.  
  400.     if (GlblShadeInfo.TwoSources) {
  401.     /* Take the absolute value of the dot product as intensity: */
  402.     Intensity = Normal[0] * GlblShadeInfo.LightSource[0] +
  403.             Normal[1] * GlblShadeInfo.LightSource[1] +
  404.             Normal[2] * GlblShadeInfo.LightSource[2];
  405.     Intensity = ABS(Intensity);
  406.     }
  407.     else {
  408.     /* Dot product of two normals is in [-1..1] range. Make it [0..1]: */
  409.     Intensity = (Normal[0] * GlblShadeInfo.LightSource[0] +
  410.              Normal[1] * GlblShadeInfo.LightSource[1] +
  411.              Normal[2] * GlblShadeInfo.LightSource[2] + 1.0) * 0.5;
  412.     }
  413.  
  414.     NewColorIndex = ColorIndex +
  415.           ((int) ((GlblShadeInfo.LevelsPerColor - 1) * (1.0 - Intensity)));
  416.     /* This should never happen, but if it does, the error is so fatal       */
  417.     /* (generate different color tone instead...) we double check this one.  */
  418.     return BOUND(NewColorIndex, ColorIndex,
  419.                 ColorIndex + GlblShadeInfo.LevelsPerColor - 1);
  420. }
  421.  
  422. /*****************************************************************************
  423. * Routine to update plane equation of the given    polygon:             *
  424. *   It is assumed that at list 3 points in polygon do exists, and pick the   *
  425. * tuple that has biggest length for maximum accuracy.                 *
  426. *   Note the polygon must be convex.                         *
  427. *   Returns FALSE if failed to evaluate the PLANE equation.             *
  428. *****************************************************************************/
  429. int UpdateEqnPolygon(IPPolygonStruct *PPolygon, int SetFlipDir)
  430. {
  431.     int    i, FlipPlaneDir;
  432.     RealType Len, V1[3], V2[3], *Coord, *CoordNext, *CoordNextNext, Plane[3],
  433.     MaxPlane[3],
  434.     MaxLen = 0.0;
  435.     IPVertexStruct
  436.     *VList = PPolygon -> PVertex;
  437.  
  438.     /* Search for 3 consequtive non-colinear point from polygon: */
  439.     for (; VList -> Pnext -> Pnext != NULL; VList = VList -> Pnext) {
  440.     Coord = VList -> Coord;
  441.     CoordNext = VList -> Pnext -> Coord;
  442.     CoordNextNext = VList -> Pnext -> Pnext -> Coord;
  443.  
  444.     if (!PT_EQ(Coord, CoordNext) &&
  445.         !PT_EQ(CoordNext, CoordNextNext)) {
  446.         for (i = 0; i < 3; i++) {/* Prepare two vectors on polygon plane.*/
  447.         V1[i] = Coord[i] - CoordNext[i];
  448.         V2[i] = CoordNext[i] - CoordNextNext[i];
  449.         }
  450.  
  451.         /* Find plane normal by a cross product of two vectors on plane: */
  452.         Plane[0] = V1[1] * V2[2] - V1[2] * V2[1];
  453.         Plane[1] = V1[2] * V2[0] - V1[0] * V2[2];
  454.         Plane[2] = V1[0] * V2[1] - V1[1] * V2[0];
  455.  
  456.         /* Find vector Len. - we are looking for the biggest: */
  457.         Len = sqrt(SQR(Plane[0]) + SQR(Plane[1]) + SQR(Plane[2]));
  458.         if (Len > MaxLen) {
  459.         for (i = 0; i < 3; i++) MaxPlane[i] = Plane[i];
  460.         MaxLen = Len;
  461.         }
  462.     }
  463.     }
  464.  
  465.     if (ABS(MaxLen) < SQR(EPSILON)) { /* Fail to find 3 non-colinear points. */
  466.     if (GlblMore) {
  467.         fprintf(stderr,
  468.         "\nError: Invalid polygon (%d) found in file (zero edge length/colinear vertices):\n",
  469.         PolyCount);
  470.         PrintPolyContent(PPolygon);
  471.     }
  472.     PPolygon -> Plane[0] = PPolygon -> Plane[1] = 0.0;  /* Pick Z = 1.0. */
  473.     PPolygon -> Plane[2] = PPolygon -> Plane[2] = 1.0;
  474.     return FALSE;
  475.     }
  476.  
  477.     if (SetFlipDir) {
  478.     FlipPlaneDir =
  479.          PPolygon -> Plane[0] * MaxPlane[0] +
  480.          PPolygon -> Plane[1] * MaxPlane[1] +
  481.          PPolygon -> Plane[2] * MaxPlane[2] < 0.0;
  482.     }
  483.     else
  484.     FlipPlaneDir = 1;
  485.  
  486.     for (i = 0; i < 3; i++)
  487.     PPolygon -> Plane[i] = (FlipPlaneDir ? -1 : 1)
  488.                         * MaxPlane[i] / MaxLen;
  489.  
  490.     PPolygon ->    Plane[3] =
  491.     (- Coord[0] * PPolygon -> Plane[0]
  492.      - Coord[1] * PPolygon -> Plane[1]
  493.      - Coord[2] * PPolygon -> Plane[2]);
  494.  
  495.     return TRUE;
  496. }
  497.  
  498. /*****************************************************************************
  499. * Routine to evaluate the cross    product    of 3 points projected to Z = 0 plane *
  500. * and return the sign of the result (Only Z component).                 *
  501. *****************************************************************************/
  502. int CrossProd(RealType Pt1[3], RealType Pt2[3], RealType Pt3[3])
  503. {
  504.     RealType Zout;
  505.  
  506.     /* U = Pt2 - Pt1,  V = Pt3 - Pt2,        Zoutput    = Ux * Vy - Uy * Vx. */
  507.     Zout = (Pt2[0] - Pt1[0]) /*    Ux */  * (Pt3[1] - Pt2[1]) /* Vy */  -
  508.        (Pt2[1] - Pt1[1]) /*    Uy */  * (Pt3[0] - Pt2[0]) /* Vx */;
  509.     if (APX_EQ(Zout, 0.0)) return 0;
  510.     if (Zout < 0.0)
  511.     return -1;
  512.     else
  513.     return 1;
  514. }
  515.  
  516. /*****************************************************************************
  517. * Routine to print the content of a given edge:                     *
  518. *****************************************************************************/
  519. void PrintPolyContent(IPPolygonStruct *PPoly)
  520. {
  521.     IPVertexStruct
  522.     *VList = PPoly -> PVertex;
  523.  
  524.     for (; VList != NULL; VList = VList -> Pnext) {
  525.     fprintf(stderr, "   %12f %12f %12f\n",
  526.         VList -> Coord[0],
  527.         VList -> Coord[1],
  528.         VList -> Coord[2]);
  529.     }
  530. }
  531.